feat(meet): Recall Calendar auto-join that replies, connect card on Meetings page, reply-name setting#4391
Conversation
…rawer and UpcomingTable - Introduced a text field for users to set their display name for meetings, which is persisted on blur. - Updated MeetDefaultsDrawer to handle the new replyDisplayName state and persist changes. - Enhanced UpcomingTable to utilize the replyDisplayName for joining meetings in reply mode. - Added tests to ensure the new functionality works as expected, including loading and persisting the display name. - Integrated RecallCalendarCard component into MeetingsPage for calendar connection management.
|
Warning Review limit reachedYou’ve reached a temporary PR review limit under our Fair Usage Limits Policy. Next review available in: 13 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughThis PR adds Recall.ai Calendar V1 support for Google Meet, including provider selection, reply-display-name persistence, Recall-backed meeting routing, and new frontend settings and integration UI. It also updates locale strings and refactors wake-word handling to recognize bare wake words. ChangesRecall Calendar Integration and Reply Anchor
Wake Word Detection Refactor
Estimated code review effort: 4 (Complex) | ~75 minutes Possibly related PRs
Suggested labels: Suggested reviewers: Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 inconclusive)
✅ Passed checks (4 passed)
Comment |
Resolve UpcomingTable.tsx handleJoin conflict: combine the reply-anchor join mode (respondToParticipant + listenOnly=!anchor) with upstream's per-join crypto.randomUUID() correlationId fix (tinyhumansai#4338). calendar_event_id stays the dedup/policy key; correlationId is now unique per join. Update the reply-mode test to assert correlationId is a fresh UUID (not the deterministic evt id), matching the new behavior.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 544b14efda
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (8)
src/openhuman/voice/always_on.rs (1)
598-642: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winDuplicated anchor-matching logic between
wake_word_presentandextract_command.Both functions independently recompute the same anchor selection (
max_by_keyon token length) and fuzzy-match scan (max_distformula,0..t.len().min(3)window). They currently agree, but any future tuning of the fuzzy-match thresholds in one function without the other will silently desync bare-wake acknowledgement from actual wake-word detection.Consider factoring the shared scan into a helper returning the matched index (e.g.
fn find_wake_anchor(transcript_tokens: &[String], wake_word: &str) -> Option<usize>), withextract_commandandwake_word_presentboth calling it.♻️ Suggested consolidation
-pub(crate) fn wake_word_present(transcript: &str, wake_word: &str) -> bool { - let wake = wake_tokens(wake_word); - if wake.is_empty() { - return false; - } - let t = wake_tokens(transcript); - let anchor = wake.iter().max_by_key(|w| w.len()).cloned().unwrap(); - let max_dist = if anchor.chars().count() <= 4 { 1 } else { 2 }; - (0..t.len().min(3)).any(|i| levenshtein(&t[i], &anchor) <= max_dist) -} +fn find_wake_anchor_index(t: &[String], wake: &[String]) -> Option<usize> { + if wake.is_empty() { + return None; + } + let anchor = wake.iter().max_by_key(|w| w.len()).cloned().unwrap(); + let max_dist = if anchor.chars().count() <= 4 { 1 } else { 2 }; + (0..t.len().min(3)).find(|&i| levenshtein(&t[i], &anchor) <= max_dist) +} + +pub(crate) fn wake_word_present(transcript: &str, wake_word: &str) -> bool { + let wake = wake_tokens(wake_word); + let t = wake_tokens(transcript); + find_wake_anchor_index(&t, &wake).is_some() +}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/openhuman/voice/always_on.rs` around lines 598 - 642, `wake_word_present` and `extract_command` duplicate the same wake-anchor selection and fuzzy scan logic, which can drift over time. Factor the shared matching into a helper in `always_on.rs` (for example, a function that finds the matched token index from the transcript tokens and wake word), then have both `wake_word_present` and `extract_command` call it so the anchor choice, distance threshold, and first-3-token window stay consistent.src/openhuman/config/ops/ui.rs (1)
277-282: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueTrim only applied via this apply path.
reply_display_nameis trimmed here onapply_meet_settings, and the downstream fallback incalendar.rsalso.trim()s before use, so double-trimming is harmless. No length/content validation is imposed on the name (arbitrary length strings are persisted to config and reused verbatim asrespondToParticipant), but since this is a user-set self-identifying label with no logging of its value shown in this diff, it's a low-priority concern.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/openhuman/config/ops/ui.rs` around lines 277 - 282, The handling of reply_display_name is normalized only in apply_meet_settings, while calendar.rs trims again before use, so make the normalization rule consistent in one place. Update apply_meet_settings and the downstream fallback that reads config.meet.reply_display_name so the same trimming behavior is applied uniformly across all paths that set or consume this field, using the existing apply_meet_settings and reply_display_name symbols to keep the logic easy to locate.src/openhuman/agent_meetings/upcoming.rs (2)
36-58: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winErrors on a connected calendar are silently swallowed to an empty list.
fetch_recall_upcomingdegrades toOk(Vec::new())for any error fromfetch_recall_meetings, not just "not connected." Contrast with the Composio branch a few lines below, which explicitly propagates per-connection API errors because "a failed list_connections is not the same as 'no connections'... it means we could not determine the connection state." For Recall, a real backend failure on an actively connected calendar (auth expiry, timeout, malformed response) will now render as "no upcoming meetings" instead of surfacing an error state to the UI.This mirrors the same swallow-to-empty pattern in
heartbeat::planner::collectors::collect_recall_calendar_meetings, so it's a deliberate, consistent choice — but it's worth confirming this UX tradeoff is intentional given the explicit error-propagation policy documented for the Composio path in this same file.💡 Possible approach: distinguish "not connected" from other failures
let meetings = match crate::openhuman::recall_calendar::ops::fetch_recall_meetings(config).await { Ok(m) => m, - Err(e) => { + Err(e) if is_not_connected_error(&e) => { tracing::info!(error = %e, "[meet:upcoming] recall calendar unavailable — skipping"); return Ok(Vec::new()); } + Err(e) => { + tracing::warn!(error = %e, "[meet:upcoming] recall meetings fetch failed"); + return Err(e); + } };This requires the backend/
IntegrationClienterror to carry a distinguishable "not connected" signal.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/openhuman/agent_meetings/upcoming.rs` around lines 36 - 58, `fetch_recall_upcoming` currently converts every `fetch_recall_meetings` failure into `Ok(Vec::new())`, which hides real backend/auth/timeout errors as “no meetings.” Update this function to distinguish the “not connected” case from other errors, and only return an empty list for the former while propagating genuine failures upward; use the existing `fetch_recall_meetings` call and `build_recall_upcoming` flow as the place to add the error handling.
107-131: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winRecall routing assumes connected when selected, and adds a status round-trip for everyone else.
Two related observations:
- When
recall_selectedis true,recall_connectedis forced totruewithout checkingis_connected(i.e., without verifying the backend'senabled/connectedflags). If the user picked Recall but the backend feature is disabled or they never completed OAuth, this still calls intofetch_recall_upcoming, which will fail and silently degrade to empty (see prior comment) instead of falling back to Composio.- When Recall is not selected, every call to
fetch_upcoming_meetingsnow performs an extrais_connected→status()network round-trip before falling back to Composio. For the majority of users who never touch Recall, this adds latency to every meetings-page poll (and, per the heartbeat collectors.rs snippet, the same round-trip is duplicated on the heartbeat tick path too).Worth considering a short-TTL cache for the Recall status check shared across the meetings-page and heartbeat paths to avoid the redundant per-poll network hit.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/openhuman/agent_meetings/upcoming.rs` around lines 107 - 131, The Recall routing in fetch_upcoming_meetings currently treats recall_selected as connected and always does a live is_connected check for everyone else, which can both misroute users and add repeated network latency. Update the upcoming.rs flow so it only chooses fetch_recall_upcoming when the Recall backend is actually enabled/connected, and otherwise cleanly falls back to Composio; keep the decision logic tied to the existing recall_selected, recall_connected, and is_connected paths. Also introduce a short-TTL shared cache for Recall status so both the meetings-page and heartbeat callers can reuse the result instead of paying the status() round-trip on every poll.app/src/lib/recallCalendar/recallCalendarApi.ts (1)
11-28: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueConsider extracting the shared CLI-envelope unwrap helper.
This duplicates the same
{ result, logs }unwrapping logic already implemented inlib/composio/composioApi.ts(per the comment). Extracting a sharedunwrapCliEnvelopeutil would avoid divergence as both evolve.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/src/lib/recallCalendar/recallCalendarApi.ts` around lines 11 - 28, The `unwrapCliEnvelope` logic in `recallCalendarApi.ts` is duplicated from the existing helper in `lib/composio/composioApi.ts`; extract this `{ result, logs }` unwrapping into a shared utility and have both `unwrapCliEnvelope` call sites reuse it. Keep the shared helper generic, preserve the current envelope checks, and update the recall calendar API to import and use the shared function instead of maintaining a second copy.app/src/lib/recallCalendar/hooks.test.ts (1)
14-40: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winConsider covering the disconnect and error-recovery paths.
Only the "already connected on first poll" scenario is tested. Given the stale-error bug flagged in
hooks.ts, a test asserting that a transientstatus()failure followed by a successful poll clears the error would have caught it, and a disconnect-flow test would improve confidence in the revert-to-composio path.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/src/lib/recallCalendar/hooks.test.ts` around lines 14 - 40, Add coverage in useRecallCalendar for the missing disconnect and error-recovery flows. Extend the hook tests around recallCalendarApi.status to simulate a transient failure followed by a successful poll and assert the hook clears the stale error state, then add a disconnect-path test that verifies it reverts provider settings back to composio and related state updates happen through openhumanUpdateMeetSettings. Focus the tests on the useRecallCalendar hook behavior rather than implementation details.app/src/lib/recallCalendar/hooks.ts (1)
34-34: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low valueUnconditional provider-flip write on first mount.
syncedProviderstarts asnull, so the very first successful poll always differs fromdesiredProviderand triggers anopenhumanUpdateMeetSettingswrite even if the backend'scalendar_provideralready matches. This is a harmless-but-needless RPC on every mount since the hook never reads the currentcalendar_providerfromstatus()/get_meet_settingsbefore deciding to flip.Also applies to: 40-51
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/src/lib/recallCalendar/hooks.ts` at line 34, The `useRecallCalendarSync` hook is writing an unnecessary provider flip on first mount because `syncedProvider` starts as `null` and always differs from `desiredProvider`. Update the initial sync logic around `syncedProvider`, `status()`, and `openhumanUpdateMeetSettings` so the hook first learns the current `calendar_provider` from the backend (via `status()`/`get_meet_settings`) before deciding whether to write. Only call the update when the backend value actually differs from the desired provider, and initialize `syncedProvider` from the real current provider to avoid the first-poll RPC.app/src/components/meetings/MeetDefaultsDrawer.tsx (1)
197-204: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winSkip the redundant save when the field is blurred without changes.
handleReplyDisplayNameBluralways callspersist(...)on blur, even if the user didn't edit the field (e.g., tab-through or click-and-away). This fires an unnecessary RPC call and flashes the saving/"Saved" status line every time, unlike the other handlers here which are wired toonChangeand only fire on an actual value change.♻️ Proposed fix to skip no-op saves
+ // Tracks the last value confirmed by the backend (initial load or a + // successful save) so blur doesn't re-persist when nothing changed. + const lastSavedReplyDisplayNameRef = useRef(''); + // Persist the display name on blur (not per keystroke). Trim before saving so // the anchor match is clean; skip the write when nothing changed. const handleReplyDisplayNameBlur = () => { const trimmed = replyDisplayName.trim(); if (trimmed !== replyDisplayName) setReplyDisplayName(trimmed); + if (trimmed === lastSavedReplyDisplayNameRef.current) return; + lastSavedReplyDisplayNameRef.current = trimmed; void persist('reply_display_name', { reply_display_name: trimmed }); };And set
lastSavedReplyDisplayNameRef.current = s.reply_display_name ?? '';alongsidesetReplyDisplayName(s.reply_display_name ?? '');in the initial load effect.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/src/components/meetings/MeetDefaultsDrawer.tsx` around lines 197 - 204, Skip the no-op save in handleReplyDisplayNameBlur: only call persist('reply_display_name', ...) when the trimmed value differs from the last saved reply display name, and keep the state in sync after trimming. Also update the initial load effect that sets replyDisplayName so lastSavedReplyDisplayNameRef.current is initialized from s.reply_display_name, ensuring blur without edits does not trigger an unnecessary RPC or status flash.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@app/src/lib/recallCalendar/hooks.ts`:
- Around line 36-63: The refresh flow in recallCalendar/hooks.ts leaves generic
status-fetch errors stuck in state after a later successful poll. Update the
refresh callback so that when recallCalendarApi.status() succeeds, any
non-provider-switch error is cleared even if syncedProvider.current already
matches the desiredProvider and the openhumanUpdateMeetSettings branch is
skipped. Keep the existing provider-switch error handling in refresh, but add a
success-path clear for stale errors so setError(String(e)) from a transient
failure does not persist forever.
In `@src/openhuman/subconscious/heartbeat/planner/collectors.rs`:
- Around line 376-403: The heartbeat collector is doing a live Recall
connectivity check on every tick in the non-Recall path, causing repeated
external RPCs. Update the logic around collect_recall_calendar_meetings and the
recall_calendar::ops::is_connected(config) fallback so the detected Recall
provider is cached or persisted to config.meet.calendar_provider after the first
successful detection. Make sure the fast path still returns immediately when
CalendarProvider::Recall is already selected, and only the initial detection
does the status lookup.
---
Nitpick comments:
In `@app/src/components/meetings/MeetDefaultsDrawer.tsx`:
- Around line 197-204: Skip the no-op save in handleReplyDisplayNameBlur: only
call persist('reply_display_name', ...) when the trimmed value differs from the
last saved reply display name, and keep the state in sync after trimming. Also
update the initial load effect that sets replyDisplayName so
lastSavedReplyDisplayNameRef.current is initialized from s.reply_display_name,
ensuring blur without edits does not trigger an unnecessary RPC or status flash.
In `@app/src/lib/recallCalendar/hooks.test.ts`:
- Around line 14-40: Add coverage in useRecallCalendar for the missing
disconnect and error-recovery flows. Extend the hook tests around
recallCalendarApi.status to simulate a transient failure followed by a
successful poll and assert the hook clears the stale error state, then add a
disconnect-path test that verifies it reverts provider settings back to composio
and related state updates happen through openhumanUpdateMeetSettings. Focus the
tests on the useRecallCalendar hook behavior rather than implementation details.
In `@app/src/lib/recallCalendar/hooks.ts`:
- Line 34: The `useRecallCalendarSync` hook is writing an unnecessary provider
flip on first mount because `syncedProvider` starts as `null` and always differs
from `desiredProvider`. Update the initial sync logic around `syncedProvider`,
`status()`, and `openhumanUpdateMeetSettings` so the hook first learns the
current `calendar_provider` from the backend (via
`status()`/`get_meet_settings`) before deciding whether to write. Only call the
update when the backend value actually differs from the desired provider, and
initialize `syncedProvider` from the real current provider to avoid the
first-poll RPC.
In `@app/src/lib/recallCalendar/recallCalendarApi.ts`:
- Around line 11-28: The `unwrapCliEnvelope` logic in `recallCalendarApi.ts` is
duplicated from the existing helper in `lib/composio/composioApi.ts`; extract
this `{ result, logs }` unwrapping into a shared utility and have both
`unwrapCliEnvelope` call sites reuse it. Keep the shared helper generic,
preserve the current envelope checks, and update the recall calendar API to
import and use the shared function instead of maintaining a second copy.
In `@src/openhuman/agent_meetings/upcoming.rs`:
- Around line 36-58: `fetch_recall_upcoming` currently converts every
`fetch_recall_meetings` failure into `Ok(Vec::new())`, which hides real
backend/auth/timeout errors as “no meetings.” Update this function to
distinguish the “not connected” case from other errors, and only return an empty
list for the former while propagating genuine failures upward; use the existing
`fetch_recall_meetings` call and `build_recall_upcoming` flow as the place to
add the error handling.
- Around line 107-131: The Recall routing in fetch_upcoming_meetings currently
treats recall_selected as connected and always does a live is_connected check
for everyone else, which can both misroute users and add repeated network
latency. Update the upcoming.rs flow so it only chooses fetch_recall_upcoming
when the Recall backend is actually enabled/connected, and otherwise cleanly
falls back to Composio; keep the decision logic tied to the existing
recall_selected, recall_connected, and is_connected paths. Also introduce a
short-TTL shared cache for Recall status so both the meetings-page and heartbeat
callers can reuse the result instead of paying the status() round-trip on every
poll.
In `@src/openhuman/config/ops/ui.rs`:
- Around line 277-282: The handling of reply_display_name is normalized only in
apply_meet_settings, while calendar.rs trims again before use, so make the
normalization rule consistent in one place. Update apply_meet_settings and the
downstream fallback that reads config.meet.reply_display_name so the same
trimming behavior is applied uniformly across all paths that set or consume this
field, using the existing apply_meet_settings and reply_display_name symbols to
keep the logic easy to locate.
In `@src/openhuman/voice/always_on.rs`:
- Around line 598-642: `wake_word_present` and `extract_command` duplicate the
same wake-anchor selection and fuzzy scan logic, which can drift over time.
Factor the shared matching into a helper in `always_on.rs` (for example, a
function that finds the matched token index from the transcript tokens and wake
word), then have both `wake_word_present` and `extract_command` call it so the
anchor choice, distance threshold, and first-3-token window stay consistent.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: a63b9ae5-ed70-4146-a4d7-cc4491b0f7d3
📒 Files selected for processing (45)
app/src/components/meetings/MeetDefaultsDrawer.tsxapp/src/components/meetings/MeetingsPage.tsxapp/src/components/meetings/UpcomingTable.tsxapp/src/components/meetings/__tests__/MeetDefaultsDrawer.test.tsxapp/src/components/meetings/__tests__/UpcomingTable.test.tsxapp/src/components/recallCalendar/RecallCalendarCard.tsxapp/src/components/recallCalendar/__tests__/RecallCalendarCard.test.tsxapp/src/lib/i18n/ar.tsapp/src/lib/i18n/bn.tsapp/src/lib/i18n/de.tsapp/src/lib/i18n/en.tsapp/src/lib/i18n/es.tsapp/src/lib/i18n/fr.tsapp/src/lib/i18n/hi.tsapp/src/lib/i18n/id.tsapp/src/lib/i18n/it.tsapp/src/lib/i18n/ko.tsapp/src/lib/i18n/pl.tsapp/src/lib/i18n/pt.tsapp/src/lib/i18n/ru.tsapp/src/lib/i18n/zh-CN.tsapp/src/lib/recallCalendar/hooks.test.tsapp/src/lib/recallCalendar/hooks.tsapp/src/lib/recallCalendar/recallCalendarApi.test.tsapp/src/lib/recallCalendar/recallCalendarApi.tsapp/src/utils/tauriCommands/config.test.tsapp/src/utils/tauriCommands/config.tssrc/core/all.rssrc/openhuman/agent_meetings/calendar.rssrc/openhuman/agent_meetings/ops.rssrc/openhuman/agent_meetings/upcoming.rssrc/openhuman/config/ops/ui.rssrc/openhuman/config/ops_tests.rssrc/openhuman/config/schema/meet.rssrc/openhuman/config/schema/mod.rssrc/openhuman/config/schemas/controllers.rssrc/openhuman/config/schemas/helpers.rssrc/openhuman/config/schemas/schema_defs.rssrc/openhuman/mod.rssrc/openhuman/recall_calendar/mod.rssrc/openhuman/recall_calendar/ops.rssrc/openhuman/recall_calendar/schemas.rssrc/openhuman/recall_calendar/types.rssrc/openhuman/subconscious/heartbeat/planner/collectors.rssrc/openhuman/voice/always_on.rs
…detection
Addresses review feedback on the Recall-calendar reply feature.
- calendar.rs auto-join: send `wakePhrase: "Hey Tiny"` whenever the bot joins
in active mode (`!listen_only`). Without it the backend has no wake gate, so
every caption from `respondToParticipant` was forwarded as an in-call command
instead of only "Hey Tiny …" requests. The bot joins as "Tiny", so the phrase
matches. Mirrors the manual reply-mode join.
- UpcomingTable reply mode: pass `wakePhrase` ("Hey <agentName>") the same way
MeetComposer does, so joining an upcoming row also gates the bot behind the
wake phrase; listen-only joins still send none.
- recallCalendar hooks: clear a stale generic status-fetch error once a later
poll succeeds, even when the provider-flip branch is skipped — a transient
network blip no longer leaves the error banner stuck forever.
- heartbeat planner: memoize the Recall connectivity probe with a 30-minute TTL
so logged-in non-Recall users stop issuing a live `status` RPC on every tick.
The settings UI still flips the provider immediately on connect; this only
rate-limits the mount-independent fallback.
Adds unit tests for each change.
…MeetingsPage - Removed unused from UpcomingTable, simplifying the logic for determining if a meeting is joined. - Updated the logic to match joined meetings based on instead of . - Added a new test suite for MeetingsPage to verify the loading of and its integration with UpcomingTable. - Enhanced existing tests for UpcomingTable to reflect the updated logic for displaying the Live pill based on . This refactor improves code clarity and ensures that the meeting joining logic is more robust and easier to maintain.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/openhuman/recall_calendar/ops.rs`:
- Around line 92-118: Clear the Recall probe cache when connection state changes
so `is_connected_cached` does not keep serving stale results. Update the
`connect` and `disconnect` flow in `recall_calendar::ops` to invalidate
`RECALL_DETECT_CACHE` immediately after a successful state change, and keep
`is_connected_cached` using the cache only for unchanged states. Also avoid
letting transient `is_connected` errors pin `false` for the full
`RECALL_DETECT_TTL` by refreshing or clearing the cached entry on error.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 4b902af2-7049-43ee-a0fc-d1d063e4afd7
📒 Files selected for processing (9)
app/src/components/meetings/UpcomingTable.tsxapp/src/components/meetings/__tests__/MeetingsPage.test.tsxapp/src/components/meetings/__tests__/UpcomingTable.test.tsxapp/src/components/recallCalendar/__tests__/RecallCalendarCard.test.tsxapp/src/lib/recallCalendar/hooks.test.tsapp/src/lib/recallCalendar/hooks.tssrc/openhuman/agent_meetings/upcoming.rssrc/openhuman/recall_calendar/ops.rssrc/openhuman/subconscious/heartbeat/planner/collectors.rs
🚧 Files skipped from review as they are similar to previous changes (5)
- app/src/components/recallCalendar/tests/RecallCalendarCard.test.tsx
- src/openhuman/agent_meetings/upcoming.rs
- app/src/components/meetings/tests/UpcomingTable.test.tsx
- app/src/components/meetings/UpcomingTable.tsx
- app/src/lib/recallCalendar/hooks.ts
is_connected_cached memoizes the Recall connectivity probe for 30 minutes so the heartbeat planner and meetings-page fallback don't issue a status RPC on every tick. But after the user connects or disconnects their calendar, the memo kept serving the pre-change value for up to the full TTL: a freshly-disconnected user would keep routing to Recall (returning an empty list) while the Composio path stayed skipped. Drop the memo at the end of a successful connect()/disconnect() via invalidate_detect_cache() so the next probe re-detects immediately. Transient status errors are still cached as not-connected to bound probing for no-session users; that staleness is now cleared the moment the connection state changes. Adds a unit test.
|
The E2E (Playwright / web lane) red is a pre-existing flake in the agent tool-loop specs, not something this branch touches. The two hard failures (latest failing job,
Why it is not caused by this PR:
Re-running the web lane should clear it. Any real fix belongs in the agent tool-loop / mock harness, not this calendar PR, so I am not touching it here. |
Summary
page (calendar connection is meeting-specific) and redesign it (icon tile, live connection
status dot, shared
Button, inline error).so wake-word ("Hey Tiny …") commands are dispatched instead of being silently dropped.
calendar_providerflag), mirroring the meetings-page fetch path.meet.reply_display_name), reused as thebot's reply anchor (
respondToParticipant) for both auto-join and "Join now".reply mode when a name is set.
Problem
After connecting Recall Calendar (#4365), the upcoming list was empty, auto-join never fired,
and even when the bot did join it never replied. Root causes:
meet.calendar_providerwas alreadyflipped — a Recall-connected user whose flag hadn't synced kept polling Composio and got
nothing.
ACTIVE_MEETINGSset, sohandle_in_call_requestdropped every reply request (config.meet.enable_in_call_agencydefaults off, and only the manual
handle_joinpath marked meetings active). This is why"Join now" replied but auto-join didn't.
listen-only.
Solution
agent_meetings/calendar.rs): theAlwaysbranchnow calls
in_call::mark_meeting_active(correlation_id)when!listen_only, exactly as themanual
handle_joinpath does — so the in-call agency dispatches replies.heartbeat/planner/collectors.rs):collect_calendar_meetingsroutes toRecall when the provider flag is set or
recall_calendar::is_connected()is true,matching
upcoming.rs.config+calendar.rs+ops.rs): new persistedmeet.reply_display_name(RPC
config_get/update_meet_settings), used as a fallback anchor for auto-join and for theAskEachTime "Join & reply" action; the manual "Join now" path passes it as
respondToParticipantand joins in reply mode (listenOnly: false) when set.RecallCalendarCard; "Live" pill inUpcomingTablederivedfrom the
backendMeetslice; new reply-name text field in the meeting-defaults drawer withpurpose-fit copy (
skills.meetingBots.replyName.*, translated across all 14 locales).(
voice/always_on.rs), instead of being ignored.Submission Checklist
for the anchor fallback + active-mode marking + wake-word matcher; Vitest for the Live
pill, "Join now" anchor/listen-only, drawer reply-name field, and the connect card.
Vitest + cargo-llvm-cov; run
pnpm test:coverageandpnpm test:rustlocally to confirmthe gate before merge.
docs/TEST-COVERAGE-MATRIX.mdstill needs the meeting-reply-name/ Recall-auto-join rows (follow-up).
## Related.docs/RELEASE-MANUAL-SMOKE.md) —N/A: no release-cut surface changed.Closes #NNNin## Related.Impact
name is set; previously they always stayed silent. Passive/listen-only joins are unchanged.
meet.reply_display_name(defaults to""); older configs parseunchanged via
#[serde(default)]. No migration.recall_calendar_statuscall perheartbeat tick only for non-Recall-flag users; negligible next to the existing calendar
fetch. No new secrets or network deps.
Related
Summary by CodeRabbit